package middleware

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"io"
	"os"
	"runtime"
	"runtime/debug"
	"runtime/trace"
	"sync"
	"sync/atomic"
)

func PrintStack() gin.HandlerFunc {
	return func(c *gin.Context) {
		//请求前
		fmt.Println("start next")
		c.Next()
		fmt.Println("end next")
		//请求后
		fmt.Println("start")
		fmt.Printf("%s", debug.Stack())
		fmt.Printf("end")
		debug.PrintStack()
	}
}

func Start() gin.HandlerFunc {
	return func(c *gin.Context) {
		var (
			err error
		)

		f, _ := os.Create("trace.txt")
		//defer f.Close()
		if err = trace.Start(f); err != nil {
			fmt.Println(err)
		}
		defer trace.Stop()

	}
}

func End() gin.HandlerFunc {
	return func(c *gin.Context) {
		trace.Stop()
	}
}

func Trace() gin.HandlerFunc {
	return func(c *gin.Context) {
		pc, _, _, ok := runtime.Caller(1)
		if !ok {
			panic("not found caller")
		}

		fn := runtime.FuncForPC(pc)
		name := fn.Name()
		fmt.Printf("enter %s\n", name)
	}
}

func StartCopy(w io.Writer) error {
	tracing.Lock()
	defer tracing.Unlock()

	if err := runtime.StartTrace(); err != nil {
		return err
	}
	go func() {
		for {
			data := runtime.ReadTrace()
			if data == nil {
				break
			}
			w.Write(data)
		}
	}()
	atomic.StoreInt32(&tracing.enabled, 1)
	return nil
}

var tracing struct {
	sync.Mutex       // gate mutators (Start, Stop)
	enabled    int32 // accessed via atomic
}

func DeepTrace(times int) gin.HandlerFunc {
	return func(c *gin.Context) {
		for skip := 0; skip <= times; skip++ {
			pc, codePath, codeLine, ok := runtime.Caller(skip)
			if !ok {
				break
				//fmt.Println("skip:", skip)
				//panic("not found caller")
			}
			fn := runtime.FuncForPC(pc)
			name := fn.Name()
			fmt.Println("name:", name, "codePath:", codePath, "codeLine", codeLine)
		}
	}
}

func DeepTraces() gin.HandlerFunc {
	return func(c *gin.Context) {
		pcs := make([]uintptr, 10)
		n := runtime.Callers(0, pcs)
		for i := 0; i < n; i++ {
			f := runtime.FuncForPC(pcs[i])
			file, line := f.FileLine(pcs[i])
			fmt.Printf("%d %s:%d %s\n", i, file, line, f.Name())
		}
	}
}

func FramesTrace() gin.HandlerFunc {
	return func(c *gin.Context) {
		pc := make([]uintptr, 10) // at least 1 entry needed
		n := runtime.Callers(0, pc)
		frames := runtime.CallersFrames(pc[:n])
		i := 0
		for {
			frame, more := frames.Next()
			fmt.Printf("%d %s:%d %s\n", i, frame.File, frame.Line, frame.Function)
			i++
			if !more {
				break
			}
		}
	}
}

func RouterTrace(c *gin.Context) {
	pcs := make([]uintptr, 10)
	n := runtime.Callers(0, pcs)
	for i := 0; i < n; i++ {
		f := runtime.FuncForPC(pcs[i])
		file, line := f.FileLine(pcs[i])
		fmt.Printf("%d %s:%d %s\n", i, file, line, f.Name())
	}
}
